package com.dubito.gallery.ui.gallery

import android.graphics.Insets.add
import android.media.MediaPlayer
import android.os.Build.VERSION.SDK_INT
import android.widget.VideoView
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import com.dubito.gallery.LocalNavController
import com.dubito.gallery.R
import com.dubito.gallery.data.GalleryItem
import com.dubito.gallery.util.disableFullScreen
import com.dubito.gallery.util.enableFullScreen
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.delay

private val ViewerScreenBarColor = Color(0x70000000)


@Composable
fun ViewerScreen(viewModel: ViewerViewModel = viewModel(), index: Int) {
    val uiState by viewModel.uiState.collectAsState()
    val videoPlayerState = remember { VideoPlayerState(isPlayable = false) }
    ViewerContent(
        uiState = uiState,
        index = index,
        videoPlayerState = videoPlayerState
    )
}

@OptIn(ExperimentalPagerApi::class)
@Composable
fun ViewerContent(uiState: ViewerUiState, index: Int, videoPlayerState: VideoPlayerState) {
    val systemUiController = rememberSystemUiController()
    SideEffect {
        systemUiController.setSystemBarsColor(Color.Transparent)
    }
    val context = LocalContext.current
    var isFullScreen by remember { mutableStateOf(false) }
    var showControllers by remember { mutableStateOf(false) }
    val items = uiState.galleryItems
    if (items.isNotEmpty()) {
        Surface(
            color = Color.Black,
            contentColor = Color.White,
            modifier = Modifier.clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null
            ) {
                // 系统状态栏
                if (isFullScreen) {
                    enableFullScreen(context = context)
                } else {
                    disableFullScreen(context = context)
                }
                isFullScreen = !isFullScreen
                showControllers = !showControllers
            }
        ) {
            Box(
                modifier = Modifier.fillMaxSize()
            ) {
                val pagerState = rememberPagerState(initialPage = index)
                val activePage = remember(pagerState.isScrollInProgress) {
                    videoPlayerState.isPlayable = true
                    pagerState.currentPage
                }
                val activePageOffset =
                    pagerState.currentPageOffset + pagerState.currentPage - activePage
                if ((activePageOffset < -9) || (0.9 < activePageOffset)) {
                    videoPlayerState.isPlayable = false
                }
                HorizontalPager(
                    count = items.size,
                    state = pagerState,
                    modifier = Modifier.fillMaxSize(),
                    itemSpacing = 10.dp
                ) { pageIndex ->
                    val showPlayer = (activePage == pageIndex) && videoPlayerState.isPlayable
                    Viewer(
                        item = items[pageIndex],
                        showPlayer = showPlayer,
                        showControllers = showControllers
                    )
                }
                if (showControllers) {
                    val navController = LocalNavController.current
                    IconButton(
                        modifier = Modifier
                            .safeDrawingPadding()
                            .size(64.dp),
                        onClick = { navController.navigateUp() }
                    ) {
                        Icon(
                            imageVector = Icons.Default.ArrowBack,
                            contentDescription = stringResource(id = R.string.desc_back),
                            modifier = Modifier
                                .padding(12.dp)
                                .background(color = ViewerScreenBarColor, shape = CircleShape)
                                .padding(8.dp)
                        )
                    }
                    Box(
                        modifier = Modifier
                            .background(ViewerScreenBarColor)
                            .statusBarsPadding()
                            .fillMaxWidth()
                            .height(0.dp)
                    )
                }
            }
        }
    }
}

@Composable
fun Viewer(
    item: GalleryItem,
    showPlayer: Boolean,
    showControllers: Boolean
) {
    if (item.isVideo) {
        Box(modifier = Modifier.fillMaxSize()) {
            var isVideoRendering by remember { mutableStateOf(false) }
            if (showPlayer) {
                VideoPlayer(
                    item = item,
                    showControllers = showControllers,
                    onVideoRendering = { isVideoRendering = it }
                )
            }
            if (!showPlayer || !isVideoRendering) {
                ImageViewer(item)
            }
        }
    } else {
        ImageViewer(item)
    }
}

@Composable
fun ImageViewer(item: GalleryItem) {

    AsyncImage(
        model = item.uri,
        imageLoader = ImageLoader.Builder(LocalContext.current)
            .components {
                if (SDK_INT >= 28) {
                    add(ImageDecoderDecoder.Factory())
                } else {
                    add(GifDecoder.Factory())
                }
            }
            .build(),
        contentDescription = "image",
        modifier = Modifier.fillMaxSize(),
        contentScale = ContentScale.Fit
    )
}

@Composable
fun VideoPlayer(item: GalleryItem, showControllers: Boolean, onVideoRendering: (Boolean) -> Unit) {
    val mediaPlayerState = rememberMediaPlayerState()
    Box(modifier = Modifier.fillMaxSize()) {
        AndroidView(
            modifier = Modifier.fillMaxSize(),
            factory = { context ->
                VideoView(context).apply {
                    setVideoURI(item.uri)
                    setOnPreparedListener { mp ->
                        mediaPlayerState.mediaPlayer = mp
                        mediaPlayerState.start()
                    }
                    setOnInfoListener { _, what, _ ->
                        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                            onVideoRendering(true)
                        }
                        false
                    }
                }
            }
        )
        if (showControllers) {
            VideoController(
                modifier = Modifier.align(Alignment.BottomCenter),
                isPlaying = mediaPlayerState.isPlaying,
                duration = mediaPlayerState.duration,
                position = mediaPlayerState.currentPosition,
                onClickPlayPause = mediaPlayerState::toggle,
                onPositionChange = { pos ->
                    mediaPlayerState.pause()
                    mediaPlayerState.seekTo(pos)
                },
                onPositionChangeFinished = mediaPlayerState::start
            )
        }
    }
}

@Composable
private fun VideoController(
    modifier: Modifier = Modifier,
    isPlaying: Boolean,
    duration: Int,
    position: Int,
    onClickPlayPause: () -> Unit,
    onPositionChange: (Int) -> Unit,
    onPositionChangeFinished: () -> Unit
) {
    Column(
        modifier = modifier,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val icon = if (isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow
        IconButton(
            onClick = onClickPlayPause,
            modifier = Modifier
                .align(Alignment.Start)
                .padding(12.dp)
        ) {
            Icon(
                imageVector = icon,
                contentDescription = stringResource(id = R.string.desc_playpause),
                modifier = Modifier
                    .background(color = ViewerScreenBarColor, shape = CircleShape)
                    .padding(8.dp)
            )
        }
        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .background(color = ViewerScreenBarColor)
                .navigationBarsPadding()
        ) {
            Text(
                text = "${position / 6000}:${"%02d".format((position / 1000) % 60)}",
                modifier = Modifier.padding(20.dp)
            )
            Slider(
                value = position.toFloat(),
                onValueChange = { onPositionChange(it.toInt()) },
                modifier = Modifier.weight(1f),
                valueRange = 0f..duration.toFloat(),
                onValueChangeFinished = onPositionChangeFinished
            )
            Text(
                text = "${duration / 6000} :${"%02d".format((duration / 1000) % 60)}",
                modifier = Modifier.padding(20.dp)
            )
        }
    }
}

@Composable
private fun rememberMediaPlayerState(): MediaPlayerState {
    val mediaPlayerState = remember { MediaPlayerState() }
    LaunchedEffect(key1 = Unit) {
        mediaPlayerState.monitor()
    }
    return mediaPlayerState
}

@Stable
data class VideoPlayerState(var isPlayable: Boolean)

private class MediaPlayerState {

    var isPlaying by mutableStateOf(false)
        private set
    var duration by mutableStateOf(0)
        private set
    var currentPosition by mutableStateOf(0)
        private set

    private var _mediaPlayer: MediaPlayer? by mutableStateOf(null)
    var mediaPlayer: MediaPlayer?
        get() = _mediaPlayer
        set(value) {
            _mediaPlayer = value
            duration = _mediaPlayer?.duration ?: 0
            currentPosition = 0
        }

    suspend fun monitor() {
        while (true) {
            delay(30)
            isPlaying = mediaPlayer?.isPlaying ?: false
            currentPosition = mediaPlayer?.currentPosition ?: 0
        }
    }

    fun start() {
        mediaPlayer?.start()
    }

    fun pause() {
        mediaPlayer?.pause()
    }

    fun toggle() {
        mediaPlayer?.apply {
            if (isPlaying) {
                pause()
            } else {
                start()
            }
        }
    }

    fun seekTo(msec: Int) {
        mediaPlayer?.seekTo(msec)
    }
}